﻿using System;
using System.Collections.Generic;

namespace PlottingBasics.LINQHelpers
{
    public static class ThinHelper
    {
        /// <summary>
        /// Will returns a sequence with ranges of elements removed that are all withing CouldBeThinned's specs. Will
        /// not remove the two end points of the elements that satisfy this range
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="CouldBeThinned"></param>
        /// <returns></returns>
        public static IEnumerable<T> Thin<T>(this IEnumerable<T> source, Func<T, T, bool> CouldBeThinned)
        {
            return new ThinItr<T>(source, CouldBeThinned);
        }

        class ThinItr<T> : IEnumerable<T>
        {
            private IEnumerable<T> _source;
            private Func<T, T, bool> _couldBeThinned;

            public ThinItr(IEnumerable<T> source, Func<T, T, bool> CouldBeThinned)
            {
                this._source = source;
                this._couldBeThinned = CouldBeThinned;
            }

            public System.Collections.Generic.IEnumerator<T> GetEnumerator()
            {
                bool first = true;
                T lastOne = default(T);

                bool inSequence = false;
                T firstOfSequence = default(T);

                foreach (var item in _source)
                {
                    T thisOne = item;
                    if (!first)
                    {
                        if (inSequence)
                        {
                            /// Going out of sequence? Put out the last good one!
                            if (!_couldBeThinned(firstOfSequence, thisOne))
                            {
                                /// If sequence was only one long - bogus!
                                yield return lastOne;
                                yield return thisOne;
                                inSequence = false;
                            }
                        }
                        else
                        {
                            /// Going into sequence?
                            if (_couldBeThinned(lastOne, thisOne))
                            {
                                firstOfSequence = lastOne;
                                inSequence = true;
                            }
                            else
                            {
                                yield return thisOne;
                            }
                        }
                    }
                    else
                    {
                        first = false;
                        yield return thisOne;
                    }

                    lastOne = thisOne;
                }

                if (inSequence)
                {
                    yield return lastOne;
                }

            }

            #region IEnumerable Members

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }

            #endregion
        }
    }
}
